package org.etnaframework.module.redis;

import org.springframework.data.redis.connection.RedisConnectionFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.UUID;

/**
 * Jedis命令使用模板
 * <p>
 * 根据http://redisdoc.com对redis操作的分类细分具体操作类。
 * 其实官网也是这样分类的https://redis.io/commands#generic
 * <p>
 * 与spring-data-redis主要不同的是，本模板更接近redis的原生操作。
 * <p>
 * 仅支持基本redis命令
 * 更新支持至Redis 3.0
 * <p>
 * Redis命令实现总共有
 * JedisCommands,
 * MultiKeyCommands,
 * AdvancedJedisCommands,
 * ScriptingCommands,
 * BasicCommands,
 * ClusterCommands,
 * SentinelCommands,
 * BinaryJedisCommands,
 * MultiKeyBinaryCommands,
 * AdvancedBinaryJedisCommands,
 * BinaryScriptingCommands
 * <p>
 * 本模板只实现了JedisCommands/MultiKeyCommands/ScriptingCommands
 * </pre>
 *
 * @author jasyaf
 * @see <a href="http://redisdoc.com/">http://redisdoc.com/</a>
 * @since 2023-11-15
 */
public class RedisTemplate {

    /**
     * Jedis客户端的唯一ID
     */
    protected final UUID id = UUID.randomUUID();

    /**
     * Jedis连接池
     */
    protected JedisPool jedisPool;

    protected RedisConnectionFactory redisConnectionFactory;

    private final RedisOpsKey key = new RedisOpsKey(this);

    private final RedisOpsString string = new RedisOpsString(this);

    private final RedisOpsHash hash = new RedisOpsHash(this);

    private final RedisOpsList list = new RedisOpsList(this);

    private final RedisOpsSet set = new RedisOpsSet(this);

    private final RedisOpsSortedSet sortedSet = new RedisOpsSortedSet(this);

    private final RedisOpsHyperLogLog hyperLogLog = new RedisOpsHyperLogLog(this);

    private final RedisOpsGeo geo = new RedisOpsGeo(this);

    private final RedisOpsPubsub pubsub = new RedisOpsPubsub(this);

    private final RedisOpsTransaction transaction = new RedisOpsTransaction(this);

    private final RedisOpsConfig config = new RedisOpsConfig(this);

    private final RedisOpsScript script = new RedisOpsScript(this);

    private final RedisOpsServer server = new RedisOpsServer(this);

    public RedisTemplate(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }

    protected Jedis getJedis() {
        return jedisPool.getResource();
    }

    /**
     * 获取jedis连接并执行(不需要使用try-with-resource)
     * <p>
     * 用法
     * <pre>
     *       jedisTemplate.execute(jedis -> {
     *          jedis.set("aa","bb");
     *       });
     * </pre>
     */
    public void execute(final JedisRunnable runnable) {
        try (Jedis jedis = getJedis()) {
            runnable.run(jedis);
        }
    }

    /**
     * 获取jedis连接并执行(不需要使用try-with-resource)
     * <p>
     * 用法
     * <pre>
     *       String result = jedisTemplate.execute(jedis -> {
     *                           return jedis.get("aa");
     *                       });
     * </pre>
     */
    public <V> V execute(final JedisCallable<V> callable) {
        try (Jedis jedis = getJedis()) {
            return callable.call(jedis);
        }
    }

    /**
     * 获取可重入锁
     * <p>
     * jedis的分布式可重入锁JedisLock Java对象实现了java.ut2il.concurrent.locks.Lock接口，同时还支持自动过期解锁。
     * <p>
     * JedisLock lock = jedisTemplate.getLock("anyLock");
     * // 最常见的使用方法
     * lock.lock();
     * <p>
     * // 支持过期解锁功能
     * // 10秒钟以后自动解锁
     * // 无需调用unlock方法手动解锁
     * lock.lock(10, TimeUnit.SECONDS);
     * <p>
     * // 尝试加锁，最多等待100秒，上锁以后10秒自动解锁
     * boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
     * ...
     * lock.unlock();
     */
    public RedisLock getLock(Object obj) {
        return new RedisLock(String.valueOf(obj), this);
    }

    /**
     * 获取可重入锁,用try with resource自动释放锁,
     * <p>
     * 例如：
     * try(JedisLock lock = jedisTemplate.lock("anyLock")) {
     * ...
     * }
     */
    public RedisLock lock(Object obj) {
        RedisLock lock = new RedisLock(String.valueOf(obj), this);
        lock.lock();
        return lock;
    }

    public RedisOpsKey key() {
        return key;
    }

    public RedisOpsString string() {
        return string;
    }

    public RedisOpsHash hash() {
        return hash;
    }

    public RedisOpsList list() {
        return list;
    }

    public RedisOpsSet set() {
        return set;
    }

    public RedisOpsSortedSet sortedSet() {
        return sortedSet;
    }

    public RedisOpsHyperLogLog hyperLogLog() {
        return hyperLogLog;
    }

    public RedisOpsGeo geo() {
        return geo;
    }

    public RedisOpsPubsub pubsub() {
        return pubsub;
    }

    public RedisOpsTransaction transaction() {
        return transaction;
    }

    public RedisOpsConfig config() {
        return config;
    }

    public RedisOpsScript script() {
        return script;
    }

    public RedisOpsServer server() {
        return server;
    }

    /**
     * jedis带返回值的执行块
     */
    @FunctionalInterface
    public interface JedisCallable<V> {

        V call(Jedis jedis);
    }

    /**
     * jedis执行块
     */
    @FunctionalInterface
    public interface JedisRunnable {

        void run(Jedis jedis);
    }
}
